import { defineStore } from 'pinia'

import api from '@/api'
import { computed, reactive } from 'vue'

// const EMPTY_SESSION = () => ({
//   username: null,
//   scope: '',
//   expires: 0,
//   access_token: '',
//   refresh_token: '',
//   authorities: [],
//   token_type: 'bearer',
//   jti: '',
//   // 以下为客户端自定义信息
//   rememberMe: false,
//   language: '',
// })

// const EMPTY_ACCOUNT = () => ({
//   id: null,
//   username: null,
//   name: '',
//   avatar: '',
//   telephone: '',
//   email: '',
//   location: '',
// })

export const useUserStore = defineStore('user', () => {

  const EMPTY_SESSION = () => ({
    username: null,
    scope: '',
    expires: 0,
    expires_in: 0,
    access_token: '',
    refresh_token: '',
    authorities: [],
    token_type: 'bearer',
    jti: '',
    // 以下为客户端自定义信息
    rememberMe: false,
    language: '',
  })

  const EMPTY_ACCOUNT = () => ({
    id: null,
    username: null,
    name: '',
    avatar: '',
    telephone: '',
    email: '',
    location: '',
  })

  const state = reactive({
    session: api.option.hasSession()
      ? Object.assign(EMPTY_SESSION(), api.option.getSession())
      : EMPTY_SESSION(),
    account: EMPTY_ACCOUNT(),
    // 收藏夹，只存在本地，暂时没有什么用
    favorite: [],
  })

  /**
   * 检查授权是否有效
   * 生效要求：持有JWT令牌，且并未超出令牌期限
   */
  const isAuthorized = computed(
    () => !!state.session.access_token && state.session.expires > new Date().getTime(),
  )
  /**
   * 检查是否管理员
   * 生效要求：已获得登录授权，并且角色中存在ROLE_ADMIN
   */
  const isAdministrator = computed(
    () => isAuthorized && state.session.authorities.includes('ROLE_ADMIN'),
  )

  /**
   * 根据过期时间刷新OAuth令牌的触发器
   */
  function refreshSessionTrigger() {
    // Session是具有有效期的，设置更新令牌的触发器
    let timeout = state.session.expires - new Date().getTime()
    if (timeout > 0) {
      console.log(`Session将在：${timeout}毫秒后过期，届时会重刷新令牌`)
      setTimeout(() => {
        refreshSession().then(() => refreshSessionTrigger())
      }, timeout)
    }
  }
  /**
   * 向服务端请求新的访问令牌
   */
  async function refreshSession() {
    try {
      let { data } = await api.auth.refresh(state.session.refresh_token)
      setupSession(data)
    } catch (e) {
      // 刷新失败，就清理掉当前的用户
      clearSession()
      console.error(e)
    }
  }

  /**
   * 登陆成功，设置登录状态
   *
   * @param session 客户端的Session
   *
   * 服务端不存储状态，登陆成功后用户状态信息通过JWT返回浏览器，存储在客户端Session中，其结构为：
   * { username, scope, expires, access_token, refresh_token, token_type, jti }
   * 另，根据用户在客户端登陆时的选项，以下内容也被添加到客户端Session中：
   * { rememberMe, location }
   */
  function setupSession(session) {
    // 服务端传来的session过期时间是相对时间（因为服务端、客户端的时间可能不一致），存储到本地时，转为绝对时间戳
    // 注意服务端返回的时间单位是秒，客户端的时间戳是毫秒
    session.expires = new Date().getTime() + session.expires_in * 1000
    Object.assign(state.session, session)
    api.option.setSession(session)
  }

  /**
   * 退出登陆状态
   * 清理vuex中的所有状态（Session、Account），以及本地存储中的Session
   */
  function clearSession() {
    state.session = EMPTY_SESSION()
    state.account = EMPTY_ACCOUNT()
    api.option.removeSession()
  }

  /**
   * 更新用户账号资料
   */
  function updateAccount(account) {
    Object.assign(state.account, account)
  }

  /**
   * 增加一项收藏
   */
  function addFavorite(id) {
    state.favorite = [...state.favorite, id]
  }

  /**
   * 删除一项收藏
   */
  function removeFavorite(id) {
    state.favorite = state.favorite.filter((x) => x !== id)
  }

  return {
    EMPTY_ACCOUNT,
    state,
    isAuthorized,
    isAdministrator,
    refreshSessionTrigger,
    refreshSession,

    setupSession,
    clearSession,
    updateAccount,

    addFavorite,
    removeFavorite
  }
})
